package com.pactera.asmp.server.service;

import com.pactera.asmp.server.common.constants.Constants;
import com.pactera.asmp.server.common.constants.ResultCode;
import com.pactera.asmp.server.common.exception.CustomException;
import com.pactera.asmp.server.dao.TCanInfoMapper;
import com.pactera.asmp.server.entity.MasterCanDto;
import com.pactera.asmp.server.pojo.TCanInfo;
import com.pactera.asmp.server.pojo.TCanInfoExample;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

/**
 * @ProjectName: tlsserver
 * @Package com.pactera.asmp.server.service
 * @ClassName: CanInfoServiceImpl
 * @Description: CanInfoServiceImpl
 * @Date 2020/06/28 17:03
 * @Author lvzhiqiang
 * @Version 1.0
 */
@Service
public class CanInfoServiceImpl implements ICanInfoService {
    @Resource
    private TCanInfoMapper canInfoMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int push(MasterCanDto masterCanDto) {
        // 校验参数
        this.checkParam(masterCanDto);

        // 判断是否在线，并分组单独处理
        List<TCanInfo> onlineCanInfos = new ArrayList<>();
        List<TCanInfo> offlineCanInfos = new ArrayList<>();
        List<TCanInfo> canInfos = masterCanDto.getCanList();
        TCanInfoExample example = new TCanInfoExample();
        for (TCanInfo canInfo : canInfos) {
            canInfo.setCuid(masterCanDto.getCuid());
            //在线
            if (Constants.CAN_INFO_STATE_1 == canInfo.getState()) {
                onlineCanInfos.add(canInfo);
                example.or()
                        .andCuidEqualTo(canInfo.getCuid())
                        .andDeviceIndexEqualTo(canInfo.getDeviceIndex())
                        .andDuidEqualTo(canInfo.getDuid())
                        .andChannelIndexEqualTo(canInfo.getChannelIndex());
                continue;
            }
            // 离线
            offlineCanInfos.add(canInfo);
        }
        return this.online(onlineCanInfos, example) + this.offline(offlineCanInfos);
    }

    /**
     * can 在线时，缓存 redis 后批量记录到数据库
     * @param onlineCanInfos
     * @return
     */
    private int online(List<TCanInfo> onlineCanInfos, TCanInfoExample example) {
        int cont = 0;
        // 集合为空时终止
        if (onlineCanInfos.isEmpty()) {
            return cont;
        }
        // 查询库中是否已存在该数据
        Map<String, Boolean> existCanInfoMap = this.queryCanInfo(example);

        // 缓存redis并对是否已存在做分组
        List<TCanInfo> notExistCanInfos = new ArrayList<>();
        for (TCanInfo canInfo : onlineCanInfos) {
            // 缓存到redis
            redisTemplate.opsForValue().set(this.handleRedisKey(canInfo),
                    String.valueOf(canInfo.getState()), Constants.REDIS_KEY_CAN_BIND_EXPIRE, TimeUnit.SECONDS);

            // 库中已存在
            if (existCanInfoMap.containsKey(this.uniqueKey(canInfo))) {
                canInfo.setState(Constants.CAN_INFO_STATE_1);
                canInfo.setLastOnlineTime(new Date());
                canInfoMapper.updateByPrimaryKeySelective2(canInfo);
                continue;
            }
            Date date = new Date();
            canInfo.setLastOnlineTime(date);
            canInfo.setCreateTime(date);
            notExistCanInfos.add(canInfo);
        }
        // 不存在时批量插入
        if (!notExistCanInfos.isEmpty()) {
            cont += canInfoMapper.batchInsert(notExistCanInfos);
        }
        return cont;
    }

    /**
     * can 离线时，删除 redis 缓存后批量将数据库中在线的改为离线
     * @param offlineCanInfos
     * @return
     */
    private int offline(List<TCanInfo> offlineCanInfos) {
        // 集合为空时终止
        if (offlineCanInfos.isEmpty()) {
            return 0;
        }

        TCanInfoExample example = new TCanInfoExample();
        for (TCanInfo canInfo : offlineCanInfos) {
            // 将当前绑定的redis缓存删除
            String redisKey = this.handleRedisKey(canInfo);
            if (redisTemplate.hasKey(redisKey).booleanValue()) {
                redisTemplate.delete(redisKey);
            }
            // 批量修改条件
            example.or()
                    .andCuidEqualTo(canInfo.getCuid())
                    .andDeviceIndexEqualTo(canInfo.getDeviceIndex())
                    .andDuidEqualTo(canInfo.getDuid())
                    .andChannelIndexEqualTo(canInfo.getChannelIndex())
                    .andStateEqualTo(Constants.CAN_INFO_STATE_1);
        }
        // 将在线状态的 can 改为离线
        TCanInfo canInfo = new TCanInfo();
        canInfo.setState(Constants.CAN_INFO_STATE_2);
        canInfo.setLastOfflineTime(new Date());
        return canInfoMapper.updateByExampleSelective(canInfo, example);
    }

    @Override
    public int heartBeat(MasterCanDto masterCanDto) {
        // 返回操作成功数
        int count = 0;
        // 校验参数
        this.checkParam(masterCanDto);
        List<TCanInfo> canInfos = masterCanDto.getCanList();
        for (TCanInfo canInfo : canInfos) {
            canInfo.setCuid(masterCanDto.getCuid());
            String redisKey = this.handleRedisKey(canInfo);
            byte state = canInfo.getState();
            //在线
            if (Constants.CAN_INFO_STATE_1 == state) {
                if (redisTemplate.hasKey(redisKey).booleanValue()
                        && redisTemplate.expire(redisKey, Constants.REDIS_KEY_CAN_BIND_EXPIRE, TimeUnit.SECONDS).booleanValue()) {
                    count += 1;
                }
                continue;
            }
            if (redisTemplate.hasKey(redisKey).booleanValue()
                    && redisTemplate.delete(redisKey).booleanValue()) {
                count += 1;
            }
        }
        return count;
    }

    /**
     * 整理 rediskey
     * @param canInfo
     * @return
     */
    private String handleRedisKey(TCanInfo canInfo) {
        return new StringBuilder(Constants.REDIS_KEY_PREFIX_CAN_BIND).append("_")
                .append(this.uniqueKey(canInfo)).toString();
    }

    private String uniqueKey(TCanInfo canInfo) {
        String cuid = canInfo.getCuid();
        Integer deviceIndex = canInfo.getDeviceIndex();
        String duid = canInfo.getDuid();
        Integer channelIndex = canInfo.getChannelIndex();
        if (null == deviceIndex || StringUtils.isBlank(duid) || null == channelIndex) {
            throw new CustomException(ResultCode.PARAM_IS_BLANK);
        }
        return new StringBuilder(cuid).append("_").append(duid)
                .append("_").append(deviceIndex)
                .append("_").append(channelIndex).toString();
    }

    /**
     * 参数校验
     * @param masterCanDto
     */
    private void checkParam(MasterCanDto masterCanDto) {
        if (null == masterCanDto || null == masterCanDto.getCanList()
                || masterCanDto.getCanList().isEmpty()
                || StringUtils.isBlank(masterCanDto.getCuid())) {
            throw new CustomException(ResultCode.PARAM_IS_BLANK);
        }
    }

    /**
     * 在线时，查询库中已存在的 can 并整理
     * @param example
     * @return
     */
    private Map<String, Boolean> queryCanInfo(TCanInfoExample example) {
        Map<String, Boolean> map = new HashMap<>();
        List<TCanInfo> existCanInfos = canInfoMapper.selectByExample(example);
        if (null != existCanInfos && !existCanInfos.isEmpty()) {
            for (TCanInfo canInfo : existCanInfos) {
                map.put(this.uniqueKey(canInfo), true);
            }
        }
        return map;
    }
}
